classdef Entity < handle
    % Entity class for managing a single entity's element information
    % 管理单个实体的所有单元信息
    
    properties
        % --- 实体基本信息 ---
        ID              % 实体ID
        Name            % 实体名称
        EntityNodeDof   % 实体节点自由度，一维杆（1），二维（2）
        
        % --- 单元信息存储 ---
        % 键值：单元类型 对： 二维数组[[单元节点连接关系]]，一维数组[材料组索引]， 一维数组[全局单元编号]
        % 单元是按照类型分类的，可以复用同一单元的形函数提高效率，但是目前并没有实现和测试
        Elements        % containers.Map: key=elementType, value=ElementData结构
        
        % --- 元数据 ---
        NumElements     % 实体包含的单元总数
    end
    
    properties (Dependent)
        ElementTypes    % 实体包含的单元类型列表
    end

    methods
        %% ===================== 构造函数 =====================
        function obj = Entity(entityID, entityName, EntityNodeDof)
            % 构造函数
            % entityID: 实体ID
            % entityName: 实体名称
            % EntityNodeDof: 实体维度
            
            obj.ID = entityID;
            obj.Name = entityName;
            obj.EntityNodeDof = EntityNodeDof;
            obj.Elements = containers.Map('KeyType', 'int32', 'ValueType', 'any');
            obj.NumElements = 0;
        end

        function NodeDof = GetEntityNodeDof(obj)
            NodeDof = obj.EntityNodeDof;
        end

        %% ===================== 单元管理方法 =====================
        
        function addElement(obj, elementType, connectivity, materialIndex, globalElementID)
            % 向实体添加单元
            % elementType: 单元类型代码
            % connectivity: 单元节点连接关系
            % materialIndex: 材料ID
            % globalElementID: 全局单元ID
            
            if ~isKey(obj.Elements, elementType)
                % 初始化该类型的单元数据
                obj.Elements(elementType) = struct(...
                    'connectivity', connectivity(:)', ...  % 单行矩阵
                    'materialIndex', materialIndex, ...
                    'globalElementIDs', globalElementID ...
                );
            else
                % 添加到现有类型
                elemData = obj.Elements(elementType);
                elemData.connectivity = [elemData.connectivity; connectivity(:)'];
                elemData.materialIndex = [elemData.materialIndex; materialIndex];
                elemData.globalElementIDs = [elemData.globalElementIDs; globalElementID];
                obj.Elements(elementType) = elemData;
            end
            
            obj.NumElements = obj.NumElements + 1;
        end
        
        function elementTypes = get.ElementTypes(obj)
            % 获取实体包含的所有单元类型
            elementTypes = keys(obj.Elements);
        end
        
        function count = getElementCount(obj, elementType)
            % 获取指定类型单元的数量
            if nargin < 2
                count = obj.NumElements;
            else
                if isKey(obj.Elements, elementType)
                    elemData = obj.Elements(elementType);
                    count = size(elemData.connectivity, 1);
                else
                    count = 0;
                end
            end
        end

        function elementTypes = getElementTypes(obj)
            % 获取实体包含的所有单元类型
            elementTypes = keys(obj.Elements);
        end

        %% ===================== 节点获取方法 =====================

        function nodeList = getAllNodes(obj)
            % 返回该实体包含的所有节点ID（去重）
            nodeList = [];
            for elemType = obj.ElementTypes
                elemData = obj.Elements(elemType{1});
                conn = elemData.connectivity;
                nodeList = [nodeList; conn(:)]; %#ok<AGROW>
            end
            nodeList = unique(nodeList);
            % 移除0占位（如果有零填充）
            nodeList(nodeList==0) = [];
        end

        %% ===================== 核心遍历方法 =====================
        
        function forEachElement(obj, globalNodeCoordinates, callback)
            % 遍历实体中的所有单元，每次输出:
            %   connectivity: 单元节点连接关系
            %   materialIndex: 材料组索引
            %   elementCoords: 单元节点坐标矩阵
            %
            % 参数:
            %   globalNodeCoordinates: 全局节点坐标表
            %   callback: 回调函数，接受 (connectivity, materialIndex, elementCoords)
            
            elementTypes = obj.getElementTypes();
            
            for i = 1:length(elementTypes)
                elemType = elementTypes{i};
                elemData = obj.Elements(elemType);
                
                nElements = size(elemData.connectivity, 1);
                for j = 1:nElements
                    connectivity = elemData.connectivity(j, :);
                    materialIndex = elemData.materialIndex(j);
                    
                    % 获取节点坐标
                    nodeIDs = connectivity(connectivity > 0);  % 移除零填充
                    elementCoords = globalNodeCoordinates(nodeIDs, :);
                    
                    % 调用回调函数
                    callback(elemType, connectivity, materialIndex, elementCoords);
                end
            end
        end

        %% ===================== 基本信息 =====================
        
        function info = getInfo(obj)
            % 获取实体基本信息
            info = struct();
            info.ID = obj.ID;
            info.Name = obj.Name;
            info.EntityNodeDof = obj.EntityNodeDof;
            info.NumElements = obj.getElementCount();
        end
        
        function displayInfo(obj)
            % 显示实体信息
            info = obj.getInfo();
            fprintf('Entity %d: "%s" (%dD, %d elements)\n', ...
                info.ID, info.Name, info.EntityNodeDof, info.NumElements);
        end
    end
end
       